﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory;

namespace SDS.SPHealthAnalyzerRules.Delegation.Helpers.ADSI
{
    public static class AD
    {

        public static bool MemberOf(string username, string groupname)
        {
            string[] UN = username.ToLower().Split('\\');
            if (UN.Length != 2)
                throw new ArgumentException(@"User name does not follow the format of CONTEXT\USERNAME");

            string[] GN = groupname.ToLower().Split('\\');
            if (GN.Length != 2)
                throw new ArgumentException(@"Group name does not follow the format of CONTEXT\USERNAME");

            // obtain a list of possible domains
            var domains = Domains().Select(d => d.ToLower());

            ContextType UserCtxType = domains.Contains(UN[0]) ? ContextType.Domain : ContextType.Machine;
            ContextType GroupCtxType = domains.Contains(GN[0]) ? ContextType.Domain : ContextType.Machine;

            UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(UserCtxType, UN[0]), UN[1]);
            GroupPrincipal group = GroupPrincipal.FindByIdentity(new PrincipalContext(GroupCtxType, GN[0], null, ContextOptions.Negotiate), GN[1]);

            // return false if user or group is null, otherwise return IsMemberOf
            return user != null &&
                   group != null &&
                   group.GetMembers(true)
                        .OfType<UserPrincipal>()
                        .Select(p => p.SamAccountName.ToLower())
                        .Contains(user.SamAccountName.ToLower());
        }
        
        public static List<string> Domains()
        {
            // Connect to RootDSE
            DirectoryEntry RootDSE = new DirectoryEntry("LDAP://rootDSE");

            // Retrieve the Configuration Naming Context from RootDSE
            string configNC = RootDSE.Properties["configurationNamingContext"].Value.ToString();

            // Connect to the Configuration Naming Context
            DirectoryEntry configSearchRoot = new DirectoryEntry("LDAP://" + configNC);

            // Search for all partitions where the NetBIOSName is set.
            DirectorySearcher configSearch = new DirectorySearcher(configSearchRoot);
            configSearch.Filter = ("(nETBIOSName=*)");

            // Configure search to return dnsroot and ncname attributes
            configSearch.PropertiesToLoad.Add("dnsroot");
            configSearch.PropertiesToLoad.Add("ncname");
            configSearch.PropertiesToLoad.Add("nETBIOSName");
            SearchResultCollection forestPartitionList = configSearch.FindAll();

            List<string> outval = new List<string>();

            // Loop through each returned domain in the result collection
            foreach (SearchResult domainPartition in forestPartitionList)
            {
                // domainName like "domain.com". ncName like "DC=domain,DC=com"
                string domainName = domainPartition.Properties["dnsroot"][0].ToString();
                string ncName = domainPartition.Properties["ncname"][0].ToString();
                string nbName = domainPartition.Properties["nETBIOSName"][0].ToString();
                outval.Add(nbName);
            }

            return outval;
        }


    } // class AD

} // namespace
